
package com.wstuo.common.dao;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Session;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.internal.CriteriaImpl;
import org.hibernate.internal.CriteriaImpl.OrderEntry;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.util.ReflectionUtils;

import com.wstuo.common.dto.PageDTO;
import com.wstuo.common.entity.BaseEntity;
import com.wstuo.common.exception.ApplicationException;
import com.wstuo.common.util.Assert;
import com.wstuo.common.util.GenericsUtils;

/**
 * @author wstuo.com
 * @param <T>
 *            the generic type of domain entity
 */
public abstract class BaseDAOImplHibernate<T> extends HibernateDaoSupport
		implements IEntityDAO<T> {
	/** LOGGER object. */
	private static final Logger LOGGER = Logger
			.getLogger(BaseDAOImplHibernate.class);

	/** the class of domain entity. */
	private Class<T> entityClass;

	/**
	 * Default Constructor.
	 */
	@SuppressWarnings("unchecked")
	public BaseDAOImplHibernate() {
        this.entityClass = GenericsUtils.getSuperClassGenricType(this.getClass(), 0);
    }

	/**
	 * Find list of entities by given ids.
	 * 
	 * @param ids
	 *            the given ids
	 * @return List<T> the list of entities
	 */
	public List<T> findByIds(Serializable[] ids) {
		Assert.notNull(ids, "fail find entities:ids is null");
		String hql = "from " + entityClass.getName() + " where "
				+ this.getIdName() + " in (:ids)";
		List<T> entities = getHibernateTemplate().findByNamedParam(hql, "ids",
				ids);

		return entities;
	}

	/**
	 * Delete the designated entities by given ids.
	 * 
	 * @param ids
	 *            the given ids
	 */
	public void deleteByIds(Serializable[] ids) {
		Assert.notNull(ids, "fail delete entities:ids is null");
		List<T> entities = findByIds(ids);

		for (T entity : entities) {
			this.delete(entity);
		}
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#save(java.lang.Object)
	 */
	public void save(T entity) {
		getHibernateTemplate().save(entity);
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#update(java.lang.Object)
	 */
	public void update(T entity) {
		getHibernateTemplate().saveOrUpdate(entity);
	}

	/**
	 * )
	 */
	public void updateAll(Collection<T> entities) {
		getHibernateTemplate().saveOrUpdateAll(entities);
	}

	/**
	 * @see com.wstuo.common.dao.IEntityDAO#merge(java.lang.Object)
	 */
	public T merge(T entity) {
		// //不能编辑系统数据 Van
		// if (entity instanceof BaseEntity ){
		//
		// BaseEntity domainEntity = (BaseEntity) entity;
		// if(domainEntity.getDataFlag()!=null &&
		// domainEntity.getDataFlag()==1){
		// throw new
		// ApplicationException("ERROR_SYSTEM_DATA_CAN_NOT_EDIT","系统数据，不能被修改~");
		// }
		// }
		//
		//
		return getHibernateTemplate().merge(entity);
	}

	/**
	 * @see com.wstuo.common.dao.IEntityDAO#mergeAll(java.util.Collection)
	 */
	public Collection<Object> mergeAll(Collection<T> entities) {
		List<Object> updatedPersistentInstances = null;

		if ((entities != null) && (entities.size() > 0)) {
			updatedPersistentInstances = new ArrayList<Object>();

			for (Object entity : entities) {
				updatedPersistentInstances.add(getHibernateTemplate().merge(
						entity));
			}
		}
		return updatedPersistentInstances;
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#delete(java.lang.Object)
	 */
	public void delete(T entity) {

		// Van
		if (entity instanceof BaseEntity) {

			BaseEntity domainEntity = (BaseEntity) entity;
			Byte dataFlag = domainEntity.getDataFlag();
			if (dataFlag != null && dataFlag.equals((byte) 1)) {
				throw new ApplicationException(
						"ERROR_SYSTEM_DATA_CAN_NOT_DELETE", "系统数据不能被删除~");
			}

		}
		getHibernateTemplate().delete(entity);
	}

	public void purge(T entity) {
		getHibernateTemplate().delete(entity);
	}

	/*
	 * @see com.wstuo.common.dao.IEntityDAO#deleteAll()
	 */
	public void deleteAll() {
		getHibernateTemplate().deleteAll(findByCriteria());
	}

	/*
	 * @see com.wstuo.common.dao.IEntityDAO#deleteAll(java.util.Collection)
	 */
	public void deleteAll(Collection<T> entities) {
		getHibernateTemplate().deleteAll(entities);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#findById(java.io.Serializable)
	 */
	public T findById(Serializable id) {
		Assert.notNull(id, "fail find entity:ids is null");
		return (T) getHibernateTemplate().get(getEntityClass(), id);
	}

	/**
	 * 如果查询结果为空，则抛出异常ERROR_DATA_NO_LONGER_EXISTS
	 * 
	 * @param id
	 * @return T
	 */
	public T findById(Serializable id, String nullException) {
		Assert.notNull(id, "fail find entity:ids is null");
		T t = getHibernateTemplate().get(getEntityClass(), id);
		if (t == null)
			throw new ApplicationException("ERROR_DATA_NO_LONGER_EXISTS",
					"数据已经不存在!");
		return t;
	}

	public T loadById(Serializable id) {
		Assert.notNull(id, "fail find entity:ids is null");
		return (T) getHibernateTemplate().load(getEntityClass(), id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#findById(java.io.Serializable,
	 * boolean)
	 */
	@SuppressWarnings("unchecked")
	public T findById(Serializable id, boolean lock) {
		Assert.notNull(id, "fail find entity:ids is null");
		T entity;

		if (lock) {
			entity = (T) getHibernateTemplate().get(getEntityClass(), id,
					LockMode.UPGRADE);
		} else {
			entity = (T) getHibernateTemplate().get(getEntityClass(), id);
		}

		return entity;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#findAll()
	 */
	public List<T> findAll() {
		return findByCriteria();
	}

	@SuppressWarnings("unchecked")
	public List<T> findByExample(T exampleInstance) {
		DetachedCriteria detachedCriteria = DetachedCriteria
				.forClass(getEntityClass());
		Example example = Example.create(exampleInstance);

		detachedCriteria.add(example);

		return getHibernateTemplate().findByCriteria(detachedCriteria);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#findByExample(java.lang.Object,
	 * java.lang.String[])
	 */
	@SuppressWarnings("unchecked")
	public List<T> findByExample(T exampleInstance,
			String... excludedProperties) {
		DetachedCriteria detachedCriteria = DetachedCriteria
				.forClass(getEntityClass());
		Example example = Example.create(exampleInstance);

		for (String excludedProperty : excludedProperties) {
			example.excludeProperty(excludedProperty);
		}

		detachedCriteria.add(example);

		return getHibernateTemplate().findByCriteria(detachedCriteria);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#findBy(java.lang.String,
	 * java.lang.Object)
	 */
	public List<T> findBy(String propertyName, Object value) {
		Assert.hasText(propertyName);

		return findByCriteria(Restrictions.eq(propertyName, value));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#findBy(java.lang.String,
	 * java.lang.Object, java.lang.String, boolean)
	 */
	public List<T> findBy(String propertyName, Object value, String orderBy,
			boolean isAsc) {
		Assert.hasText(propertyName);
		Assert.hasText(orderBy);

		return findByCriteria(orderBy, isAsc,
				Restrictions.eq(propertyName, value));
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#findUniqueBy(java.lang.String,
	 * java.lang.Object)
	 */
	@SuppressWarnings("unchecked")
	public T findUniqueBy(String propertyName, Object value) {
		Assert.hasText(propertyName);
		T result = null;
		DetachedCriteria dc = createCriteria(Restrictions.eq(propertyName,
				value));
		List<T> entities = getHibernateTemplate().findByCriteria(dc);

		if (entities.size() > 0) {
			result = entities.get(0);
		}
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.wstuo.common.dao.IEntityDAO#isUnique(java.lang.Object,
	 * java.lang.String)
	 */
	public boolean isUnique(T entity, String uniquePropertyNames) {
		Assert.hasText(uniquePropertyNames);

		DetachedCriteria criteria = createCriteria().setProjection(
				Projections.rowCount());
		String[] namesList = uniquePropertyNames.split(",");

		try {
			for (String name : namesList) {
				criteria.add(Restrictions.eq(name,
						PropertyUtils.getProperty(entity, name)));
			}

			// if id!=null, then object has already been exist, add judge for
			// except itself?
			String idName = getIdName();
			Serializable id = getId(entity);

			if (id != null) {
				criteria.add(Restrictions.not(Restrictions.eq(idName, id)));
			}
		} catch (Exception e) {
			ReflectionUtils.handleReflectionException(e);
		}

		return (Integer) getHibernateTemplate().findByCriteria(criteria).get(0) == 0;
	}

	/**
	 * A convenience method to get the identifier of the persistent instance.
	 * 
	 * @param entity
	 * 
	 * @return Serializable
	 */
	protected Serializable getId(T entity) throws NoSuchMethodException,
			IllegalAccessException, InvocationTargetException {
		Assert.notNull(entity);
		Assert.notNull(entityClass);

		return (Serializable) PropertyUtils.getProperty(entity, getIdName());
	}

	/**
	 * A convenience method to get the identifier name of the persistent
	 * instance.
	 * 
	 * @return String
	 */
	protected String getIdName() {
		Assert.notNull(getEntityClass());

		ClassMetadata meta = getSessionFactory().getClassMetadata(
				getEntityClass());

		Assert.notNull(meta, "Class " + getEntityClass()
				+ " not define in hibernate session factory.");

		String idName = meta.getIdentifierPropertyName();

		Assert.hasText(idName, getEntityClass().getSimpleName()
				+ " has no identifier property define.");

		return idName;
	}

	/**
	 * Use this inside subclasses as a convenience method.
	 * 
	 * @param criterions
	 * @return List<T>
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findByCriteria(Criterion... criterions) {
		DetachedCriteria detachedCriteria = DetachedCriteria
				.forClass(getEntityClass());
		for (Criterion criterion : criterions) {
			detachedCriteria.add(criterion);
		}

		if (BaseEntity.class.isAssignableFrom(entityClass)) {
			detachedCriteria.add(Restrictions.ne("dataFlag",
					BaseEntity.DELETED.byteValue()));
		}

		return getHibernateTemplate().findByCriteria(detachedCriteria);
	}

	/**
	 * Use this inside subclasses as a convenience method.
	 * 
	 * @param criterions
	 * @return List<T>
	 */
	@SuppressWarnings("unchecked")
	protected List<T> findByCriteria(String orderBy, boolean isAsc,
			Criterion... criterions) {
		DetachedCriteria detachedCriteria = DetachedCriteria
				.forClass(getEntityClass());

		for (Criterion criterion : criterions) {
			detachedCriteria.add(criterion);
		}

		if (isAsc) {
			detachedCriteria.addOrder(Order.asc(orderBy));
		} else {
			detachedCriteria.addOrder(Order.desc(orderBy));
		}

		if (BaseEntity.class.isAssignableFrom(entityClass)) {
			detachedCriteria.add(Restrictions.ne("dataFlag",
					BaseEntity.DELETED.byteValue()));
		}

		return getHibernateTemplate().findByCriteria(detachedCriteria);
	}

	/**
	 * A convenience method to create a Criteria instance managed by the current
	 * transactional Session.
	 * 
	 * @param criterions
	 */
	protected DetachedCriteria createCriteria(Criterion... criterions) {
		DetachedCriteria criteria = DetachedCriteria.forClass(getEntityClass());

		for (Criterion criterion : criterions) {
			criteria.add(criterion);
		}

		return criteria;
	}

	/**
	 * A convenience method to create a Criteria instance managed by the current
	 * transactional Session, and with an ordering.
	 * 
	 */
	protected DetachedCriteria createCriteria(String orderBy, boolean isAsc,
			Criterion... criterions) {
		Assert.hasText(orderBy);

		DetachedCriteria criteria = createCriteria(criterions);

		if (isAsc) {
			criteria.addOrder(Order.asc(orderBy));
		} else {
			criteria.addOrder(Order.desc(orderBy));
		}

		return criteria;
	}

	/**
	 * @return the entityClass
	 */
	public Class<T> getEntityClass() {
		return entityClass;
	}

	// -----------------------------------------------------------------
	/**
	 * Find the designated page data by given criteria.
	 * 
	 * @param detachedCriteria
	 *            detachedCriteria
	 * @param start
	 *            the start record no. of the page
	 * @param limit
	 *            the pagesize
	 * @return the designated page data
	 */
	public PageDTO findPageByCriteria(final DetachedCriteria detachedCriteria,
			final int start, final int limit) {
		if (BaseEntity.class.isAssignableFrom(entityClass)) {
			detachedCriteria.add(Restrictions.ne("dataFlag",
					BaseEntity.DELETED.byteValue()));
		}
		return (PageDTO) getHibernateTemplate().execute(
				new HibernateCallback<PageDTO>() {
					public PageDTO doInHibernate(Session session) {
						Boolean result=false;
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);
						// Get the orginal orderEntries
						OrderEntry[] orderEntries = getOrders(criteria);
						// Remove the orders
						criteria = removeOrders(criteria);

						// get the original projection
						Projection projection = getProjection(criteria);
						int totalCount=0;
						try {
							if(criteria.setProjection(Projections.rowCount()).uniqueResult()!=null){
								totalCount = ((Number) criteria.setProjection(
									Projections.rowCount()).uniqueResult())
									.intValue();
							}
						} catch (Exception e) {
							
						}

						criteria.setProjection(projection);
						CriteriaImpl impl = (CriteriaImpl)criteria;
						ClassMetadata meta = getSessionFactory().getClassMetadata(impl.getEntityOrClassName());
						String idName=(meta==null)?null:meta.getIdentifierPropertyName();
						if (projection == null) {
							// Set the ResultTransformer to get the same object
							// structure with hql
							result=true;
							criteria.setProjection(Projections.property(idName).as("id"));
							criteria.setResultTransformer(CriteriaSpecification.PROJECTION);
						}

						// Add the orginal orderEntries
						criteria = addOrders(criteria, orderEntries);

						criteria.setFirstResult(start);
						criteria.setMaxResults(limit);
						criteria.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);//数据结果不重复
						List<Long> items =new ArrayList<Long>();
						if(idName!=null)
							items=criteria.list();
						Long[] num = new Long[items.size()];
					    for(int idx = 0; idx < items.size(); idx++){
					    	num[idx] = items.get(idx);
					    }
					    if(result){
					    	criteria.setProjection(null);
						    criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
					    }
						criteria.setFirstResult(0).setMaxResults(totalCount);
						PageDTO pageDTO = new PageDTO();
						pageDTO.setTotalSize(totalCount);
						
						Criteria criteriaData = detachedCriteria.getExecutableCriteria(session);
						Projection projectionData = getProjection(criteriaData);
						if (projectionData == null) {
							// Set the ResultTransformer to get the same object
							criteriaData.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
						}
						if(idName!=null && num!=null && num.length>0){
							criteriaData.add(Restrictions.in(idName, num));
						}else{
							criteriaData.setFirstResult(start);
							criteriaData.setMaxResults(limit);
						}
						criteriaData.setResultTransformer(CriteriaSpecification.DISTINCT_ROOT_ENTITY);//数据结果不重复
						List itemsData = criteriaData.list();
						pageDTO.setData(itemsData);
						return pageDTO;
					}
				});
	}

	// findKnowledgeDAO
	public PageDTO findPageByCriteria(final DetachedCriteria detachedCriteria,
			final int start, final int limit, final int findKnowledgeIds) {
		if (BaseEntity.class.isAssignableFrom(entityClass)) {
			detachedCriteria.add(Restrictions.ne("dataFlag",
					BaseEntity.DELETED.byteValue()));
		}
		return (PageDTO) getHibernateTemplate().execute(
				new HibernateCallback<PageDTO>() {
					public PageDTO doInHibernate(Session session) {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);

						// Get the orginal orderEntries
						OrderEntry[] orderEntries = getOrders(criteria);
						// Remove the orders
						criteria = removeOrders(criteria);

						// get the original projection
						Projection projection = getProjection(criteria);

						int totalCount = ((Number) criteria.setProjection(
								Projections.rowCount()).uniqueResult())
								.intValue();

						criteria.setProjection(projection);

						if (projection == null) {
							// Set the ResultTransformer to get the same object
							// structure with hql
							criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
						}

						// Add the orginal orderEntries
						criteria = addOrders(criteria, orderEntries);

						criteria.setFirstResult(start);
						criteria.setMaxResults(limit);
						criteria.setResultTransformer(DetachedCriteria.DISTINCT_ROOT_ENTITY);// 数据结果不重复
						List items = criteria.list();
						totalCount = items.size();

						criteria.setFirstResult(0).setMaxResults(totalCount);

						PageDTO pageDTO = new PageDTO();

						pageDTO.setTotalSize(totalCount);
						pageDTO.setData(items);

						return pageDTO;
					}
				});
	}

	public PageDTO findPageByCriteria(final DetachedCriteria detachedCriteria,
			final int start, final int limit, final String propertyName) {
		if (BaseEntity.class.isAssignableFrom(entityClass)) {
			detachedCriteria.add(Restrictions.ne("dataFlag",
					BaseEntity.DELETED.byteValue()));
		}

		return (PageDTO) getHibernateTemplate().execute(
				new HibernateCallback<PageDTO>() {
					public PageDTO doInHibernate(Session session) {
						Boolean result=false;
						Criteria criteria = detachedCriteria.getExecutableCriteria(session);
						
						CriteriaImpl impl = (CriteriaImpl)criteria;
						ClassMetadata meta = getSessionFactory().getClassMetadata(impl.getEntityOrClassName());
						String idName=meta.getIdentifierPropertyName();
						// Get the orginal orderEntries
						OrderEntry[] orderEntries = getOrders(criteria);
						// Remove the orders
						criteria = removeOrders(criteria);

						// get the original projection
						Projection projection = getProjection(criteria);

						int totalCount = ((Number) criteria.setProjection(
								Projections.countDistinct(idName)).uniqueResult())
								.intValue();

						criteria.setProjection(projection);
						
						if (projection == null) {
							// Set the ResultTransformer to get the same object
							// structure with hql
							result=true;
							criteria.setProjection(Projections.property(idName).as("id"));
							criteria.setResultTransformer(CriteriaSpecification.PROJECTION);
						}
						// Add the orginal orderEntries
						criteria = addOrders(criteria, orderEntries);
						
						criteria.setFirstResult(start);
						criteria.setMaxResults(limit);
						//查询出分页后的实体ID
						List<Long> items =new ArrayList<Long>();
						if(idName!=null){
							items=criteria.list();
						}
						
					    if(result){
					    	criteria.setProjection(null);
						    criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
					    }
					    List itemsData=new ArrayList();
						//二次查询
						Criteria criteriaDate = detachedCriteria.forClass(meta.getMappedClass()).getExecutableCriteria(session);
						if(idName!=null && items!=null && items.size()>0){
							//根据分页后的实体ID获取需要的实体
							criteriaDate.add(Restrictions.in(idName, items));
							for (OrderEntry order : orderEntries) {
								criteriaDate.addOrder(order.getOrder());
							}
							itemsData = criteriaDate.list();
						}
						PageDTO pageDTO = new PageDTO();
						pageDTO.setTotalSize(totalCount);
						pageDTO.setData(itemsData);
						return pageDTO;
					}
			});
	}
	
	
	public PageDTO findPageByCriteria(final DetachedCriteria detachedCriteria,
			final int start, final int limit, final String propertyName, final String[] propertyNames) {
		if (BaseEntity.class.isAssignableFrom(entityClass)) {
			detachedCriteria.add(Restrictions.ne("dataFlag",
					BaseEntity.DELETED.byteValue()));
		}

		return (PageDTO) getHibernateTemplate().execute(
				new HibernateCallback<PageDTO>() {
					public PageDTO doInHibernate(Session session) {
						Boolean result=false;
						Criteria criteria = detachedCriteria.getExecutableCriteria(session);
						
						CriteriaImpl impl = (CriteriaImpl)criteria;
						ClassMetadata meta = getSessionFactory().getClassMetadata(impl.getEntityOrClassName());
						String idName=meta.getIdentifierPropertyName();
						// Get the orginal orderEntries
						OrderEntry[] orderEntries = getOrders(criteria);
						// Remove the orders
						criteria = removeOrders(criteria);

						// get the original projection
						Projection projection = getProjection(criteria);

						int totalCount = ((Number) criteria.setProjection(
								Projections.countDistinct(idName)).uniqueResult())
								.intValue();

						criteria.setProjection(projection);
						
						if (projection == null) {
							// Set the ResultTransformer to get the same object
							// structure with hql
							result=true;
							ProjectionList projectionList = Projections.projectionList();
							projectionList.add(Projections.distinct(Projections.property(idName).as("id")));
							for (String string : propertyNames) {
								projectionList.add(Projections.property(string));
							}
							criteria.setProjection(projectionList);
							criteria.setResultTransformer(CriteriaSpecification.PROJECTION);
						}
						// Add the orginal orderEntries
						criteria = addOrders(criteria, orderEntries);
						
						criteria.setFirstResult(start);
						criteria.setMaxResults(limit);
						//查询出分页后的实体ID
						List<Object[]> objs = new ArrayList<Object[]>();
						List<Long> items =new ArrayList<Long>();
						if(idName!=null){
							objs=criteria.list();
						}
						for (Object[] obj : objs) {
							items.add(obj[0]!=null?Long.parseLong(obj[0].toString()):0L);
						}
						
					    if(result){
					    	criteria.setProjection(null);
						    criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
					    }
					    //二次查询
						Criteria criteriaData = detachedCriteria.forClass(meta.getMappedClass()).getExecutableCriteria(session);
						List itemsData=new ArrayList();
						if(idName!=null && items!=null && items.size()>0){
							//根据分页后的实体ID获取需要的实体
							criteriaData.add(Restrictions.in(idName, items));
							criteriaData.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
							for (OrderEntry order : orderEntries) {
								criteriaData.addOrder(order.getOrder());
							}
							criteriaData.setResultTransformer(DetachedCriteria.DISTINCT_ROOT_ENTITY);//数据结果不重复
							itemsData = criteriaData.list();
						}
						PageDTO pageDTO = new PageDTO();
						pageDTO.setTotalSize(totalCount);
						pageDTO.setData(itemsData);
						return pageDTO;
					}
			});
	}
	
	public List findByCriteria(final DetachedCriteria detachedCriteria,
			final int start, final int limit, final String propertyName) {
		if (BaseEntity.class.isAssignableFrom(entityClass)) {
			detachedCriteria.add(Restrictions.ne("dataFlag",
					BaseEntity.DELETED.byteValue()));
		}

		return  getHibernateTemplate().executeFind(
				new HibernateCallback<List>() {
					public List doInHibernate(Session session) {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);

						// Get the orginal orderEntries
						OrderEntry[] orderEntries = getOrders(criteria);
						// Remove the orders
						criteria = removeOrders(criteria);

						// get the original projection
						Projection projection = getProjection(criteria);

						criteria.setProjection(projection);
						if (projection == null) {
							// Set the ResultTransformer to get the same object
							// structure with hql
							criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
						}

						// Add the orginal orderEntries
						criteria = addOrders(criteria, orderEntries);
						criteria.setResultTransformer(DetachedCriteria.DISTINCT_ROOT_ENTITY);// 数据结果不重复

						List items1 = criteria.list();
						return items1;
					}
				});
	}

	/**
	 * Retrieves projection from the criteria. Since no method declared in the
	 * interface, so fetch it from the implementation
	 * 
	 * @see CriteriaImpl#getProjection()
	 * @param criteria
	 *            the criteria
	 * @return the Projection
	 */
	private static Projection getProjection(Criteria criteria) {
		assertType(criteria);

		CriteriaImpl impl = (CriteriaImpl) criteria;

		return impl.getProjection();
	}

	private static void assertType(Criteria criteria) {
		Assert.notNull(criteria, " criteria is required. ");

		String message = criteria + "";

		if (!CriteriaImpl.class.isInstance(criteria) && LOGGER.isDebugEnabled()) {
			LOGGER.debug(message);
		}
	}

	/**
	 * Retrieves array of OrderEntry from the criteria.
	 * 
	 * @param criteria
	 *            the criteria
	 * @return the OrderEntry[]
	 */
	private static OrderEntry[] getOrders(Criteria criteria) {
		assertType(criteria);

		CriteriaImpl impl = (CriteriaImpl) criteria;
		Field field = getOrderEntriesField(criteria);

		try {
			return (OrderEntry[]) ((List) field.get(impl))
					.toArray(new OrderEntry[0]);
		} catch (Exception e) {
			logAndThrowException(criteria, e);
			throw new InternalError(
					" Runtime Exception impossibility can't throw ");
		}
	}

	/**
	 * Remove OrderEntrys from the criteria
	 * 
	 * @param criteria
	 *            the criteria
	 * @return the criteria after removed OrderEntry[]
	 */
	private static Criteria removeOrders(Criteria criteria) {
		assertType(criteria);

		CriteriaImpl impl = (CriteriaImpl) criteria;

		try {
			Field field = getOrderEntriesField(criteria);

			field.set(impl, new ArrayList());

			return impl;
		} catch (Exception e) {
			logAndThrowException(criteria, e);
			throw new InternalError(
					" Runtime Exception impossibility can't throw ");
		}
	}

	/**
	 * Add OrderEntrys to the criteria
	 * 
	 * @param criteria
	 *            the criteria
	 * @param orderEntries
	 *            the OrderEntry[]
	 * @return the criteria after add OrderEntry[]
	 */
	private static Criteria addOrders(Criteria criteria,
			OrderEntry[] orderEntries) {
		assertType(criteria);

		CriteriaImpl impl = (CriteriaImpl) criteria;

		try {
			Field field = getOrderEntriesField(criteria);

			for (int i = 0; i < orderEntries.length; i++) {
				List innerOrderEntries = (List) field.get(criteria);

				innerOrderEntries.add(orderEntries[i]);
			}
			return impl;
		} catch (Exception e) {
			logAndThrowException(criteria, e);
			throw new InternalError(
					" Runtime Exception impossibility can't throw ");
		}
	}

	private static void logAndThrowException(Criteria criteria, Exception e) {
		String message = criteria + "";

		if (LOGGER.isDebugEnabled()) {
			LOGGER.debug(message, e);
		}
	}

	private static Field getOrderEntriesField(Criteria criteria) {
		Assert.notNull(criteria, " criteria is requried. ");

		try {
			Field field = CriteriaImpl.class.getDeclaredField("orderEntries");

			field.setAccessible(true);

			return field;
		} catch (Exception e) {
			logAndThrowException(criteria, e);
			throw new InternalError();
		}
	}

	/**
	 * 用于统计
	 * 
	 * @param detachedCriteria
	 * @return Integer
	 */
	public Integer statCriteria(final DetachedCriteria detachedCriteria) {
		if (BaseEntity.class.isAssignableFrom(entityClass)) {
			detachedCriteria.add(Restrictions.ne("dataFlag",
					BaseEntity.DELETED.byteValue()));
		}
		return (Integer) getHibernateTemplate().execute(
				new HibernateCallback<Integer>() {
					public Integer doInHibernate(Session session) {
						Criteria criteria = detachedCriteria
								.getExecutableCriteria(session);

						// Get the orginal orderEntries
						OrderEntry[] orderEntries = getOrders(criteria);
						// Remove the orders
						criteria = removeOrders(criteria);

						// get the original projection
						// Projection projection = getProjection( criteria );
						int totalCount = ((Number) criteria.setProjection(
								Projections.rowCount()).uniqueResult())
								.intValue();
						return totalCount;
					}
				});
	}
	
	/**
	 * Hql 分页
	 * @param queryString
	 * @param start
	 * @param limit
	 * @return PageDTO
	 */
	public PageDTO findPageByHql(final String queryString,final int start, final int limit){
		return (PageDTO) getHibernateTemplate().execute(
				new HibernateCallback<PageDTO>() {
					public PageDTO doInHibernate(Session session) {
                List items = session.createQuery(queryString)
                                     .setFirstResult(start)
                                     .setMaxResults(limit)
                                     .list();
                PageDTO pageDTO = new PageDTO();
				pageDTO.setData(items);
                return pageDTO;
            }
        });
	}
	
	/**
	 * Sql 分页
	 * @param queryString
	 * @param start
	 * @param limit
	 * @return PageDTO
	 */
	public PageDTO findPageBySql(final String queryString,final int start, final int limit){
		return (PageDTO) getHibernateTemplate().execute(
				new HibernateCallback<PageDTO>() {
					public PageDTO doInHibernate(Session session) {
                List items = session.createSQLQuery(queryString)
                                     .setFirstResult(start)
                                     .setMaxResults(limit)
                                     .list();
                PageDTO pageDTO = new PageDTO();
				pageDTO.setData(items);
                return pageDTO;
            }
        });
	}

}
